package com.fuhao.mvcframework.servlet;

import com.fuhao.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * Created by fuhao on 2019/10/28.
 */
public class FHDispatcherServlet extends HttpServlet {

    private Properties contextConfig = new Properties();

    private List<String> classNames = new ArrayList<String>();

    private Map<String,Object> ioc = new HashMap<String,Object>();

    private Map<String,Method> handlerMapping = new HashMap<String,Method>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //调用doGet或者doPost 反射调用，将结果输出到浏览器
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception"+ Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        if(this.handlerMapping.isEmpty()){ return; }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"").replaceAll("/+","/");

        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found！！");
            return;
        }

        Method method = this.handlerMapping.get(url);

        Map<String,String[]> params = req.getParameterMap();

        String beanName = lowerFisrtCase(method.getDeclaringClass().getSimpleName());
        String name = null;
        if(params.containsKey("name")){
            name = params.get("name")[0];
        }
        method.invoke(ioc.get(beanName),new Object[]{req,resp,name});
    }


    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2、解析配置文件，扫描所有相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
        //3、初始化所有的相关的类，并且保存到IOC容器之中
        doInstance();
        //4、完成自动化的依赖注入（DI）
        doAutowired();
        //5、创建HanderMapping将url和method建立对应关系
        initHandlerMappping();

        System.out.print("FH Spring MVC is init");
    }

    private void initHandlerMappping() {
        if(ioc.isEmpty()){ return; }

        for(Map.Entry<String, Object> entry: ioc.entrySet()){

            Class<?> clazz = entry.getValue().getClass();

            if(!clazz.isAnnotationPresent(FHController.class)) { continue; }

            String baseUrl = "";
            if(clazz.isAnnotationPresent(FHRequestMapping.class)){
                FHRequestMapping requestMapping = clazz.getAnnotation(FHRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            Method[] methods = clazz.getMethods();
            for(Method method : methods){

                if(!method.isAnnotationPresent(FHRequestMapping.class)){ continue; }

                FHRequestMapping requestMapping =  method.getAnnotation(FHRequestMapping.class);
                String url = ("/"+ baseUrl + "/"+requestMapping.value()).replaceAll("/+","/");
                handlerMapping.put(url,method);

                System.out.print("Mapped:"+url+","+method);
            }
        }
    }

    private void doAutowired() {
        if(ioc.isEmpty()){ return; }

        for(Map.Entry<String, Object> entry: ioc.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();

            for(Field field : fields){
                //判断字段上是否加注解
                if(!field.isAnnotationPresent(FHAutowired.class)){ continue; }

                FHAutowired autowired = field.getAnnotation(FHAutowired.class);
                String beanName = autowired.value();
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }


                field.setAccessible(true);//强制授权

                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doInstance() {
        if(classNames.isEmpty()){ return; }

        for(String className : classNames){
            try {
                Class<?> clazz = Class.forName(className);
                //过滤Spring注解
                if(clazz.isAnnotationPresent(FHController.class)){
                    String beanName = lowerFisrtCase(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(FHService.class)){
                    //1、类名首字母小写
                    //2、自定义命名
                    FHService service = clazz.getAnnotation(FHService.class);
                    String beanName = service.value();
                    if("".equals(beanName.trim())){
                        beanName = lowerFisrtCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName,clazz.newInstance());
                    //3、用接口的全称作为key，用接口的实现类作为值
                    Class<?> [] interfaces = clazz.getInterfaces();
                    for(Class i : interfaces){
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("The beanName is exists");
                        }
                        ioc.put(i.getName(),instance);
                    }

                }else {
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private String lowerFisrtCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }

    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/"));
        File classDir = new File(url.getFile());
        for(File file : classDir.listFiles()){
            //递归判断
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else{
                if(!file.getName().endsWith(".class")){ continue; }
                String className = (scanPackage+"."+file.getName().replace(".class","")).trim();
                classNames.add(className);
            }
        }
    }

    private void doLoadConfig(String contextConfigLocation) {
        //从类路径下取得properties
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null!=is){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
